﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using MvcEngine.Mvc.Models.ViewModels;
using System.Web.Mvc.Html;
using MvcEngine.Core.Extensions;
using MvcEngine.Core.Helpers;
using MvcEngine.Core.Content;
using MvcEngine.Core;
using MvcEngine.Core.Repositories;
using MvcEngine.Mvc.StarterSite.Controllers;
using MvcEngine.Mvc.Controllers;
using System.Linq.Expressions;
using System.Web.Routing;
using System.IO;
using MvcEngine.Mvc.Models;
using MvcEngine.Mvc.Filters;
using MvcEngine.Core.Exceptions;

namespace MvcEngine.Mvc.Extensions
{
    public static class RenderPartialExtensions
    {
        public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, List<ControlData> data)
        {
            htmlHelper.RenderPartial(partialViewName, new MvcEngineModelItem<List<ControlData>>() { Item = data });
        }

        public static string GetPartial(this HtmlHelper htmlHelper, string partialViewName, object model)
        {
            Guard.ThrowIfEmptyString(partialViewName, "partialViewName can't be null or empty string.");

            var mvcEngineModel = model as MvcEngineModel;
            if (mvcEngineModel != null)
            {
                var parentModel = htmlHelper.ViewData.Model as MvcEngineModel;
                if (parentModel != null)
                {
                    mvcEngineModel.AntiForgeryToken = parentModel.AntiForgeryToken;
                }
            }

            ViewDataDictionary newViewData = new ViewDataDictionary(model);
            TextWriter writer = new StringWriter();
            ViewContext newViewContext = new ViewContext(htmlHelper.ViewContext, htmlHelper.ViewContext.View, newViewData, htmlHelper.ViewContext.TempData, writer);

            var viewResult = ViewEngines.Engines.FindPartialView(newViewContext, partialViewName);
            if (viewResult.View != null)
            {
                viewResult.View.Render(newViewContext, writer);
            }
            return writer.ToString();
        }

        public static void RenderEditor(this HtmlHelper htmlHelper, string partialViewName, string name, string content)
        {
            htmlHelper.RenderPartial(partialViewName, new EditorViewData() { Name = name, Content = content });
        }

        public static void RenderControl(this HtmlHelper htmlHelper, string content)
        {
            var controls = GetModuleControls(htmlHelper, content);
            controls.ForEach(a => a.Action());
        }

        public static void RenderControl(this HtmlHelper htmlHelper)
        {
            string action = (string)htmlHelper.ViewContext.RouteData.Values["actionName"];

            var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
            var ctrl = (BaseController)controllerFactory.CreateController(htmlHelper.ViewContext.RequestContext, (string)htmlHelper.ViewContext.RouteData.Values["controllerName"]);

            var values = new Dictionary<string, object>();
            values["moduleName"] = htmlHelper.ViewContext.RouteData.Values["moduleName"];

            Action act = ctrl.GetResult(htmlHelper.ViewContext.Controller, null, action, false, values);
            act();
        }

        public static void RenderAction<T>(this HtmlHelper htmlHelper, string action, object routeValues)
        {
            Type controllerType = typeof(T);

            BaseController ctrl = (BaseController)IoC.Resolve(controllerType);

            Action act = ctrl.GetResult(htmlHelper.ViewContext.Controller, controllerType.Namespace, action, false, new RouteValueDictionary(routeValues));
            act();
        }

        public static void RenderAction<T>(this HtmlHelper htmlHelper, Expression<Action<T>> action)
        {
            Type controllerType = typeof(T);

            BaseController ctrl = (BaseController)IoC.Resolve(controllerType);

            MethodCallExpression methodCall = action.Body as MethodCallExpression;


            var obj = (((System.Linq.Expressions.MemberExpression)(((System.Linq.Expressions.MemberExpression)(methodCall.Arguments[0])).Expression))).Member;

            var value = (((System.Linq.Expressions.MemberExpression)((Expression)methodCall.Arguments[0])).Member as System.Reflection.PropertyInfo).GetValue(obj, null);

            IDictionary<string, object> values = new Dictionary<string, object>();
            int i = 0;
            foreach (var item in methodCall.Method.GetParameters())
            {
                var r = GetUnaryExpressionFromMethodCall(methodCall.Arguments[0]);
                values.Add(item.Name, (methodCall.Arguments.OfType<ConstantExpression>().ElementAt(i)).Value);
                i++;
            }

            Action act = ctrl.GetResult(htmlHelper.ViewContext.Controller, controllerType.Namespace, methodCall.Method.Name, false, new RouteValueDictionary(values));
            act();
        }

        public static UnaryExpression GetUnaryExpressionFromMethodCall(Expression expression)
        {
            MethodCallExpression mCall = expression as MethodCallExpression;
            UnaryExpression uExp = null;

            foreach (Expression exp in mCall.Arguments)
            {
                if (exp is UnaryExpression)
                {
                    uExp = exp as UnaryExpression;
                    break;
                }
                else if (exp is MethodCallExpression)
                {
                    uExp = GetUnaryExpressionFromMethodCall(exp);
                    break;
                }
            }
            return uExp;
        }


        private static List<ControlData> GetModuleControls(HtmlHelper htmlHelper, string content)
        {
            Page page = null;
            PageContent pageContent = null;

            var action = (string)htmlHelper.ViewContext.RouteData.Values["actionName"];

            var ctrIdValue = htmlHelper.ViewContext.RouteData.Values["ctrId"];
            if (ctrIdValue != null)
            {
                int? ctrId = ctrIdValue.ToString().ToInt();
                if (ctrId.HasValue)
                {
                    pageContent = ContentManager.Current.GetPageContent(ctrId.Value);
                }
            }

            var pageNameValue = htmlHelper.ViewContext.RouteData.Values["pageName"];
            if (pageNameValue == null)
            {
                var pageIdValue = htmlHelper.ViewContext.RouteData.Values["pageId"];
                if (pageIdValue != null)
                {
                    var pageId = pageIdValue.ToString().ToGuid();
                    page = ContentManager.Current.GetPage(pageId);
                }
            }
            else
            {
                page = ContentManager.Current.GetPage(pageNameValue.ToString());
            }

            if (page == null)
            {
                return new List<ControlData>();
            }

            var pageContentRepository = IoC.Resolve<IPageContentRepository>();
            IList<PageContentDetails> contents = pageContentRepository.GetPageContentDetailsByPage(page.Id).Where(c => c.ContainerMarkupId == content).ToList();

            List<ControlData> controls = new List<ControlData>();

            foreach (var item in contents)
            {
                if (!PermissionHelper.Current.HasRights(item.Id, PermissionTypes.View))
                    continue;

                Control control = ContentManager.Current.GetControl(item.ControlId);

                if (!control.Visible)
                    continue;

                Type controlServiceType = Type.GetType(control.ControlServiceType);
                IControlService controlService = (IControlService)IoC.Resolve(controlServiceType);

                var controllerType = controlService.GetControllerType();

                BaseController ctrl = (BaseController)IoC.Resolve(controllerType);
                Dictionary<string, object> values = GetRouteValues(control.ModuleId, item.Id, item.ReferenceId);

                values["controlId"] = control.Id;
                Action act = null;

                if (pageContent != null && item.Id == pageContent.Id && !string.IsNullOrEmpty(action))
                {
                    act = ctrl.GetResult(htmlHelper.ViewContext.Controller, controllerType.Namespace, action, false, values);
                }
                else
                {
                    string defaultAction = controlService.GetDefaultAction();
                    if (string.IsNullOrEmpty(defaultAction))
                        throw new BusinessException("Every controller in the Starter Site must have a method marked with 'DefaultActionAttribute'.");
                    act = ctrl.GetResult(htmlHelper.ViewContext.Controller, controllerType.Namespace, defaultAction, true, values);
                }

                if (act != null)
                {
                    var data = new ControlData()
                    {
                        Action = act,
                        ActionName = action,
                        Control = control,
                        PageContentId = item.Id,
                        ControllerType = controllerType,
                        ContentName = item.ContentName
                    };
                    controls.Add(data);
                }
            }
            return controls;
        }



        public static void RenderControl(this HtmlHelper htmlHelper, string content, string partialViewNameWrapper)
        {
            var controls = GetModuleControls(htmlHelper, content);
            foreach (var item in controls)
            {
                var view = ViewEngines.Engines.FindPartialView(htmlHelper.ViewContext.Controller.ControllerContext, partialViewNameWrapper);
                htmlHelper.ViewContext.ViewData.Model = new MvcEngineModelItem<ControlData>() { Item = item };
                view.View.Render(htmlHelper.ViewContext, htmlHelper.ViewContext.HttpContext.Response.Output);
            }
        }

        public static void RenderBackOfficeMarkup(this HtmlHelper htmlHelper, string content, Guid pageId)
        {
            var pageContentRepository = IoC.Resolve<IPageContentRepository>();
            IList<PageContentDetails> contents = pageContentRepository.GetPageContentDetailsByPage(pageId).Where(c => c.ContainerMarkupId == content).ToList();
            foreach (var item in contents)
            {

                var control = ContentManager.Current.GetControl(item.ControlId);
                var controlViewModel = new ControlViewModel() { Name = item.ContentName, ControlId = control.Id, CtrId = item.Id };
                controlViewModel.Commands = CommandRegistrator.Current.GetCommands(item.ControlId).Where(c => c.Visible)
                    .Select(c => new ControlCommandViewModel() { Name = c.Name, LocalizedName = c.LocalizedName });

                var view = ViewEngines.Engines.FindPartialView(htmlHelper.ViewContext.Controller.ControllerContext, "ControlMarkup");
                htmlHelper.ViewContext.ViewData.Model = new MvcEngineModelItem<ControlViewModel>() { Item = controlViewModel };
                view.View.Render(htmlHelper.ViewContext, htmlHelper.ViewContext.HttpContext.Response.Output);
            }
        }

        private static Dictionary<string, object> GetRouteValues(Guid moduleId, int ctrId, int? ctrReferenceId)
        {
            Dictionary<string, object> values = new Dictionary<string, object>();
            values["ctrId"] = ctrId;
            values["moduleName"] = ContentManager.Current.GetModule(moduleId).Name;
            if (ctrReferenceId.HasValue)
                values["ctrReferenceId"] = ctrReferenceId.Value;
            return values;
        }
    }
}
